package com.example.scale;

import org.andengine.engine.camera.Camera;
import org.andengine.entity.sprite.AnimatedSprite;
import org.andengine.extension.physics.box2d.PhysicsConnector;
import org.andengine.extension.physics.box2d.PhysicsFactory;
import org.andengine.extension.physics.box2d.PhysicsWorld;
import org.andengine.opengl.texture.region.ITiledTextureRegion;
import org.andengine.opengl.texture.region.TiledTextureRegion;
import org.andengine.opengl.vbo.VertexBufferObjectManager;

import android.util.Log;

import com.badlogic.gdx.math.Vector2;
import com.badlogic.gdx.physics.box2d.Body;
import com.badlogic.gdx.physics.box2d.BodyDef.BodyType;

// Constructor
public abstract class Player extends AnimatedSprite {
	public Body body;
	public float playerScale = .2f;
	
	// Boolean variables for the direction the character is facing, and moving
	public boolean goingRight = false;
	public boolean goingLeft = false;
	public boolean facingRight = true;
	public boolean facingLeft = false;
	
	// Boolean variables for which directions the character is colliding
	public boolean collidingRight = false;
	public boolean collidingLeft = false;
	public boolean collidingTop = false;
	public boolean collidingBottom = false;
	
	// Integer variables for the number of contacts the character is colliding with
	public int collideRightNum = 0;
	public int collideLeftNum = 0;
	public int collideTopNum = 0;
	public int collideBottomNum = 0;
	
	public int footContacts; // Stores contact count
	
	public boolean alive = true;
	
	// Scaling Variables
	public boolean scaling = false;			// If the character is currently scaled
	public float scaleMeterAmount = 100f; 		// Current scale amount left
	public float maxScaleMeterAmount = 100f;	// Maximum scale amount
	public float scaleDecrementAmount = 0.4f;
	public float timeWaited = 0f;
	public float maxTimeWaited = 100f;
	
	// Enum
	public static final int STOPPED = 0;
	public static final int WALKING = 1;
	public static final int RUNNING = 2;
	public static final int JUMPING_UP = 3;
	public static final int JUMPING_FORWARD = 4;
	public static final int FALLING = 5;
	public int playerState = 0;

	public Player(float pX, float pY, ITiledTextureRegion textureRegion, VertexBufferObjectManager vbo,
			Camera camera, PhysicsWorld physicsWorld) {
		super(pX, pY, textureRegion, vbo);
	}

	public abstract void onDie();
	
	/*public void setTiledTextureRegion(ITiledTextureRegion newRegion) {
	    this.mTextureRegion = newRegion;
	    //this.onUpdateColor();
        //this.onUpdateVertices();
        //this.onUpdateTextureCoordinates();
	}*/

	public void createPhysics(final Camera camera, PhysicsWorld physicsWorld) {
		// Initialize player body
		body = PhysicsFactory.createBoxBody(physicsWorld, this,
				BodyType.DynamicBody, PhysicsFactory.createFixtureDef(0, 0, .01f)); // Could be 0, .05, .02

		body.setUserData("player");
		body.setFixedRotation(true);

		// Register physics connect so sprite will automatically update its
		// position
		/*physicsWorld.registerPhysicsConnector(new PhysicsConnector(this, body,
				true, false) {
			// Use onUpdate method to help reduce camera jittering when moving
			@Override
			public void onUpdate(float pSecondsElapsed) {
				super.onUpdate(pSecondsElapsed);
				camera.onUpdate(0.1f);

				// Check if character falls beneath the level
				if(body.getPosition().y <= GameScene.getInstance().minCameraY) {
					onDie();
				}
				//Log.d("Player Position", Float.toString(this.getEntity().getX()));
				for(int i=0; i<GameScene.getInstance().popupTable.length; i++) {
					//Log.d(Boolean.toString((Boolean) GameScene.getInstance().popupTable[i][0]), Integer.toString((Integer) GameScene.getInstance().popupTable[i][1]));
					if((Boolean) GameScene.getInstance().popupTable[i][0]==false && this.getEntity().getX() >= (Integer) GameScene.getInstance().popupTable[i][1]) {
						//Log.d("Popup table info", "POPUP TABLE INFO");
						//Log.d("X Position: ", Integer.toString((Integer) GameScene.getInstance().popupTable[i][1]));
						//Log.d("Delay: ", Integer.toString((Integer) GameScene.getInstance().popupTable[i][2]));
						//Log.d("Text: ", (String) GameScene.getInstance().popupTable[i][3]);
						GameScene.getInstance().popupTable[i][0]=true;
						GameScene.getInstance().popupText.setText((String) GameScene.getInstance().popupTable[i][3]);
						//Log.d("Popping up", Float.toString((Float) GameScene.getInstance().popupTable[i][2]));
						GameScene.getInstance().showPopup((Integer) GameScene.getInstance().popupTable[i][2]);
					}
				}
				//if (getY() <= 0) {
				//	onDie();
				//}

				//if (canRun) {
					//body.setLinearVelocity(new Vector2(5, body
							//.getLinearVelocity().y));
				//}
			}
		}); */
	}
	
	public void changeScale(float newScale) {
		setScale(newScale);
		final PhysicsConnector pc = GameScene.physicsWorld
				.getPhysicsConnectorManager()
				.findPhysicsConnectorByShape(this);
		GameScene.physicsWorld.unregisterPhysicsConnector(pc);
		GameScene.physicsWorld.destroyBody(body);
		createPhysics(GameScene.camera, GameScene.physicsWorld);
		GameScene.physicsWorld
				.registerPhysicsConnector(new PhysicsConnector(
						this, body, true,
						false) {
					// Use onUpdate method to help
					// reduce
					// camera jittering when moving
					@Override
					public void onUpdate(
							float pSecondsElapsed) {
						super.onUpdate(pSecondsElapsed);
						GameScene.camera.onUpdate(0.1f);
					}
				});
	}
	
	public void changeScaleXY(float newXScale, float newYScale) {
		//setScaleCenterY(0.5f);
		setScaleY(newYScale);
		setScaleX(newXScale);
		final PhysicsConnector pc = GameScene.physicsWorld
				.getPhysicsConnectorManager()
				.findPhysicsConnectorByShape(this);
		GameScene.physicsWorld.unregisterPhysicsConnector(pc);
		GameScene.physicsWorld.destroyBody(body);
		createPhysics(GameScene.camera, GameScene.physicsWorld);
		GameScene.physicsWorld
				.registerPhysicsConnector(new PhysicsConnector(
						this, body, true,
						false) {
					// Use onUpdate method to help
					// reduce
					// camera jittering when moving
					@Override
					public void onUpdate(
							float pSecondsElapsed) {
						super.onUpdate(pSecondsElapsed);
						GameScene.camera.onUpdate(0.1f);
					}
				});
	}
	
	public void changeScaleX(float newScale) {
		setScaleX(newScale);
		/*final PhysicsConnector pc = GameScene.physicsWorld
				.getPhysicsConnectorManager()
				.findPhysicsConnectorByShape(this);
		GameScene.physicsWorld.unregisterPhysicsConnector(pc);
		GameScene.physicsWorld.destroyBody(body);
		createPhysics(GameScene.camera, GameScene.physicsWorld);
		GameScene.physicsWorld
				.registerPhysicsConnector(new PhysicsConnector(
						this, body, true,
						false) {
					// Use onUpdate method to help
					// reduce
					// camera jittering when moving
					@Override
					public void onUpdate(
							float pSecondsElapsed) {
						super.onUpdate(pSecondsElapsed);
						GameScene.camera.onUpdate(0.1f);
					}
				});*/
	}
	
	public void changeScaleY(float newScale) {
		setScaleY(newScale);
		final PhysicsConnector pc = GameScene.physicsWorld
				.getPhysicsConnectorManager()
				.findPhysicsConnectorByShape(this);
		GameScene.physicsWorld.unregisterPhysicsConnector(pc);
		GameScene.physicsWorld.destroyBody(body);
		createPhysics(GameScene.camera, GameScene.physicsWorld);
		GameScene.physicsWorld
				.registerPhysicsConnector(new PhysicsConnector(
						this, body, true,
						false) {
					// Use onUpdate method to help
					// reduce
					// camera jittering when moving
					@Override
					public void onUpdate(
							float pSecondsElapsed) {
						super.onUpdate(pSecondsElapsed);
						GameScene.camera.onUpdate(0.1f);
					}
				});
	}
	
	public void changeVelocity(float xVelocity, float yVelocity) {
		body.setLinearVelocity(new Vector2(
				xVelocity, yVelocity));
	}

	public void setRunning(float xVel, int newState) {
		//canRun = true;
		// Define duration for each of player's tile in ms
		/*int timeFrame = 125;
		final long[] PLAYER_ANIMATE = new long[] { timeFrame, timeFrame, timeFrame, timeFrame, timeFrame };
		// Set player to animate (Continuously because of true parameter), animate from tile 0 to 1
		if (goingRight) {
			animate(PLAYER_ANIMATE, 1, 5, true);
		} else {
			animate(PLAYER_ANIMATE, 7, 11, true);
		}*/
		changeVelocity(xVel,
				body.getLinearVelocity().y);
		// Adjust the player's animation if he's changed state
		if (newState != playerState) {
			if (newState == RUNNING) {
				setPlayerAnimation("running");
				animate(80, 0, 12, true);
			} else {
				setPlayerAnimation("jumpingForward");
				animate(250, 0, 7, false);
			}
			playerState = newState;
		}
	}
	
	public void setRunning() {
		changeVelocity(body.getLinearVelocity().x,
				body.getLinearVelocity().y);
		// Adjust the player's animation if he's changed state
		//if (playerState != RUNNING) {
			setPlayerAnimation("running");
			animate(80, 0, 12, true);
			playerState = RUNNING;
		//}
	}
	
	public void stopRunning() {
		changeVelocity(0, body.getLinearVelocity().y);
		if (playerState != STOPPED) {
			setPlayerAnimation("stopped");
			playerState = STOPPED;
		}
		stopAnimation(0);
		goingLeft = false;
		goingRight = false;
	}
	
	public void setJumping() {
		/*int timeFrame = 250;
		final long[] PLAYER_ANIMATE = new long[] { timeFrame, timeFrame };
		// Set player to animate (Continuously because of true parameter), animate from tile 0 to 1
		if (facingRight) {
			if(!goingRight) { 	// Facing right, but not moving
				stopAnimation(12);
				animate(PLAYER_ANIMATE, 12, 13, false);
			} else {			// Moving right
				stopAnimation(14);
			}
		} else {
			if(!goingLeft) {	// Facing left, but not moving
				stopAnimation(15);
				animate(PLAYER_ANIMATE, 15, 16, false);
			} else {			// Moving left
				stopAnimation(17);
			}
		}*/
		if (playerState == JUMPING_FORWARD) {
			animate(100, 3, 10, false);
		} else if (playerState == JUMPING_UP) {
			animate(80, 0, 5, false);
		}
	}
	
	public void jump()
	{
		if (!goingRight && !goingLeft) {
			if (playerState != JUMPING_UP) {
				playerState = JUMPING_UP;
				setPlayerAnimation("jumpingUp");
			}
			//player.animate(250, 0, 5, false);
		} else {
			if (playerState != JUMPING_FORWARD) {
				playerState = JUMPING_FORWARD;
				setPlayerAnimation("jumpingForward");
			}
			//player.animate(100, 0, 10, false);
		}
		
		if (footContacts >= 1) {
			this.setJumping();
			// Set linear velocity on y axis
			body.setLinearVelocity(new Vector2(body.getLinearVelocity().x, (float) (Math.sqrt(getScaleY())*13)));
		}  else if (footContacts == 0) {
			// Allow for double jump
			this.setJumping();
			body.setLinearVelocity(new Vector2(body.getLinearVelocity().x, (float) (body.getLinearVelocity().y+Math.sqrt(getScaleY())*10)));
			footContacts=-1;
		}
	    //body.setLinearVelocity(new Vector2(body.getLinearVelocity().x, 12));
	}

	public void setFalling() {
		if (playerState != FALLING) {
			if (playerState != JUMPING_FORWARD) {
				setPlayerAnimation("jumpingForward");
			}
			playerState = FALLING;
			stopAnimation(11);
		}
	}
	
	public void land() {
		if (!goingRight && !goingLeft) {
			if (playerState == FALLING) {
				stopRunning();
				return;
			} else if (playerState != JUMPING_UP) {
				playerState = JUMPING_UP;
				setPlayerAnimation("jumpingUp");
			}
			//animate(50, 6, 10, false);
			final long duration = 50;
			final long[] PLAYER_ANIMATE = new long[] { duration, duration, duration, duration, duration };
			animate(PLAYER_ANIMATE, 6, 10, false, new IAnimationListener(){

				@Override
				public void onAnimationFinished(AnimatedSprite pAnimatedSprite) {
					stopRunning();
				}

				@Override
				public void onAnimationStarted(AnimatedSprite pAnimatedSprite,
						int pInitialLoopCount) {					
				}

				@Override
				public void onAnimationFrameChanged(
						AnimatedSprite pAnimatedSprite, int pOldFrameIndex,
						int pNewFrameIndex) {
				}

				@Override
				public void onAnimationLoopFinished(
						AnimatedSprite pAnimatedSprite,
						int pRemainingLoopCount, int pInitialLoopCount) {
				}

                });
		} else {
			if (playerState == FALLING) {
				setRunning();
				return;
			} else if (playerState != JUMPING_FORWARD) {
				playerState = JUMPING_FORWARD;
				setPlayerAnimation("jumpingForward");
			}
			//animate(40, 11, 17, false);
			//setRunning();
			
			// Animate the landing, then transition to running
			final long duration = 40;
			final long[] PLAYER_ANIMATE = new long[] { duration, duration, duration, duration, duration/*, duration, duration*/ };
			animate(PLAYER_ANIMATE, 11, 15, false, new IAnimationListener(){

				@Override
				public void onAnimationFinished(AnimatedSprite pAnimatedSprite) {
					setRunning();
				}

				@Override
				public void onAnimationStarted(AnimatedSprite pAnimatedSprite,
						int pInitialLoopCount) {
				}

				@Override
				public void onAnimationFrameChanged(
						AnimatedSprite pAnimatedSprite, int pOldFrameIndex,
						int pNewFrameIndex) {
				}

				@Override
				public void onAnimationLoopFinished(
						AnimatedSprite pAnimatedSprite,
						int pRemainingLoopCount, int pInitialLoopCount) {
				}

                });
		}
		//int timeFrame = 125;
		//final long[] PLAYER_ANIMATE = new long[] { timeFrame, timeFrame };
		/*if (facingRight) {
			//animate(PLAYER_ANIMATE, new int[] {13, 12}, false);
			//animate(PLAYER_ANIMATE, 13, 13, false);
			//animate(PLAYER_ANIMATE, 12, 12, false);
			if(!goingRight) {
				stopAnimation(0);
			} else {
				setRunning();
			}
		} else {
			//animate(PLAYER_ANIMATE, new int[] {16, 15}, false);
			//animate(PLAYER_ANIMATE, 16, 16, false);
			//animate(PLAYER_ANIMATE, 15, 15, false);
			if(!goingLeft) {
				stopAnimation(6);
			} else {
				setRunning();
			}
		}*/
	}
	
	public void increaseFootContacts() {
		Log.d("Increase foot contacts", "Increase foot contacts from: "+footContacts);
		//this.land();
		if (playerState == FALLING || playerState == JUMPING_UP || playerState == JUMPING_FORWARD) {
			this.land();
		}
		if(footContacts > 0) {
			++footContacts;
		} else {
			footContacts = 1;
		}
		//footContacts=1;
		Log.d("Increase foot contacts", "Increase foot contacts to: "+footContacts);
	}
	
	public void decreaseFootContacts() {
		Log.d("Decrease foot contacts", "Decrease foot contacts from: "+footContacts);
		--footContacts;
		Log.d("Decrease foot contacts", "Decrease foot contacts to: "+footContacts);
		//footContacts=0;
	}

	/* getWidth() and getHeight() return the original size of the sprite.
	 getScaledWidth() and getScaledHeight() return the current size */
	public float getScaledWidth()
	{
	    return this.getScaleX() * this.getWidth();
	}
	 
	public float getScaledHeight()
	{
	    return this.getScaleY() * this.getHeight();
	}
	
	public void setPlayerAnimation(String animationType) {
		// TODO: Is this the problem?
		if (animationType == "walking") {
			ResourcesManager.getInstance().switchToWalkingTexture();
		} else if (animationType == "running") {
			//player.setTiledTextureRegion(resourcesManager.player_running_region);
			ResourcesManager.getInstance().switchToRunningTexture();
		} else if (animationType == "stopped" || animationType == "pushing") {
			//player.setTiledTextureRegion(resourcesManager.player_stopped_region);
			ResourcesManager.getInstance().switchToStoppedTexture();
		} else if (animationType == "jumpingUp") {
			ResourcesManager.getInstance().switchToJumpingUpTexture();
		} else if (animationType == "jumpingForward") {
			ResourcesManager.getInstance().switchToJumpingForwardTexture();
		} else {
			// Hoisting
			ResourcesManager.getInstance().switchToHoistingTexture();
		}
		//player.setTiledTextureRegion(resourcesManager.player_texture_region);
		/*float x = player.getX();
		float y = player.getY();
		//player.setVisible(false);
		player.detachSelf();
		
		ITiledTextureRegion animationRegion;
		if (animationType == "walking") {
			animationRegion = resourcesManager.player_walking_region;
		} else if (animationType == "running") {
			animationRegion = resourcesManager.player_running_region;
		} else if (animationType == "stopped" || animationType == "pushing") {
			animationRegion = resourcesManager.player_stopped_region;
		} else if (animationType == "jumpingUp") {
			animationRegion = resourcesManager.player_jumping_up_region;
		} else if (animationType == "jumpingForward") {
			animationRegion = resourcesManager.player_jumping_run_region;
		} else {
			// Hoisting
			animationRegion = resourcesManager.player_hoisting_region;
		}
		// Get data from player before recreating
		int tempFootContacts = player.footContacts;
		boolean tempGoingRight = player.goingRight;
		boolean tempGoingLeft = player.goingLeft;
		boolean tempFacingRight = player.facingRight;
		boolean tempFacingLeft = player.facingLeft;
		boolean tempCollidingRight = player.collidingRight;
		boolean tempCollidingLeft = player.collidingLeft;
		boolean tempCollidingTop = player.collidingTop;
		boolean tempCollidingBottom = player.collidingBottom;
		int tempColRightNum = player.collideRightNum;
		int tempColLeftNum = player.collideLeftNum;
		int tempColTopNum = player.collideTopNum;
		int tempColBottomNum = player.collideBottomNum;
		
		
		//bool 
		player = new Player(x, y, animationRegion, 
				vbom, camera, physicsWorld) {
			@Override
			public void onDie() {
				if (!gameOverDisplayed
						&& !levelEndDisplayed) {
					player.alive = false;
					if (player.footContacts >= 1) {
						player.stopRunning();
					}
					displayGameOverText();
					// Hide HUD
					// camera.getHUD().setVisible(false);
				}
			}
		};
		//player.setScale(player.playerScale);
		player.createPhysics(camera, physicsWorld);
		player.footContacts=tempFootContacts;
		player.goingRight = tempGoingRight;
		player.goingLeft = tempGoingLeft;
		player.facingRight = tempFacingRight;
		player.facingLeft = tempFacingLeft;
		player.collidingRight = tempCollidingRight;
		player.collidingLeft = tempCollidingLeft;
		player.collidingTop = tempCollidingTop;
		player.collidingBottom = tempCollidingBottom;
		player.collideRightNum = tempColRightNum;
		player.collideLeftNum = tempColLeftNum;
		player.collideTopNum = tempColTopNum;
		player.collideBottomNum = tempColBottomNum;
	    //attachObjectWithNewPosition(player, x, y);
	    attachChild(player);
		
		
		// From initial instantiation of player
		camera.setChaseEntity(player); // Camera will follow player's position
		player.changeScale(0.2f);*/
	}

	public void animate(int timeAmount, int pFirstTileIndex, int pLastTileIndex,
			boolean pLoop) {
		// Generate an array for animation so that an int can be passed to animate for the duration of a frame
		int numFrames = pLastTileIndex - pFirstTileIndex + 1;
		long[] timeArray = new long[numFrames];
		for (int i=0; i < numFrames; i++) {
			timeArray[i] = timeAmount;
		}
		animate(timeArray, pFirstTileIndex, pLastTileIndex, pLoop);
	}

	public void flipSpriteLeft() {
		Log.d("Flipping goddamned sprite left", "Left Scale: "+Float.toString(-Math.abs(this.getScaleX())));
		changeScaleX(-Math.abs(this.getScaleX()));
		//this.changeScale(-1.0f);
	}

	public void flipSpriteRight() {
		Log.d("Flipping sprite right", "Right Scale: "+Float.toString(Math.abs(this.getScaleX())));
		changeScaleX(Math.abs(this.getScaleX()));
	}
}